﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 仪表识别2
{
    class MeterImageProcess : ImageProcess
    {
        public Image<Bgr, byte> _image;
        public TabManager tabManager { get; set; }

        public MeterImageProcess(Image<Bgr, byte> image)
        {
            base._image = image;
            this._image = image;
        }


        public override void proccess()
        {

            //long num1 = 12345678;
            //long num2 = 12346587;
            //string s1 = num1+"";
            //string s2 = num2 + "";
            //int len = 0;
            //for(int i=0;i<s1.Length;i++)
            //{

            //        if (s1[i] == s2[i])
            //        {
            //            len++;
            //        }
            //        else
            //        {
            //            break;
            //        }
            //}
            //Log.info("长度:"+len);
            while (true)
            {
                
                //拷贝一份
                Image<Bgr, byte> image = Images._image.Copy();// _image.Copy();
                ImageParams.width = image.Width;
                ImageParams.height = image.Height;
                Log.debug("开始处理图像...");
                long start = DateTime.Now.Millisecond;
                Log.debug("开始预处理...");
                Image<Gray, byte> grayImage = toGray(image);
                if (ImageShowStatus.showGray)
                    tabManager.addTabPageWithPicBox("灰度图", grayImage.Bitmap);
                else tabManager.remove("灰度图");
                Image<Gray, byte> pretreatmentImage = pretreatment(image);
                if (ImageParams.dilate > 0)
                {
                    pretreatmentImage=pretreatmentImage.Dilate(ImageParams.dilate);
                }
                if (ImageParams.erode > 0)
                {
                    pretreatmentImage=pretreatmentImage.Erode(ImageParams.erode);
                }
                if (ImageShowStatus.showFilter)
                    tabManager.addTabPageWithPicBox("预处理图像", pretreatmentImage.Bitmap);
                else tabManager.remove("预处理图像");
                Image<Gray, byte> binImage = toBin(pretreatmentImage, ImageParams.binThreshold, ImageParams.binMaxValue);
                if(ImageShowStatus.showBin)
                    tabManager.addTabPageWithPicBox("二值化图像", binImage.Bitmap);
                else tabManager.remove("二值化图像");
                Image<Gray, byte> cannyImage = toCanny(pretreatmentImage, ImageParams.cannyThresh, ImageParams.cannyThreshLinking);
                if(ImageShowStatus.showCanny)
                    tabManager.addTabPageWithPicBox("边缘图像", cannyImage.Bitmap);
                else tabManager.remove("边缘图像");
                Image <Gray, byte> binCannyImage = toCanny(binImage, ImageParams.cannyThresh, ImageParams.cannyThreshLinking);
                //tabManager.addTabPageWithPicBox("二值化边缘图像", binCannyImage.Bitmap);
                VectorOfVectorOfPoint vvp = null;
                //Image<Gray, byte> contoursImage = findContours(binImage, ref vvp);
                //tabManager.addTabPageWithPicBox("轮廓图", contoursImage.Bitmap);
                Image<Gray, byte> laplaceImage = laplace(pretreatmentImage, ImageParams.laplaceApertureSize);
                Image<Gray, byte> laplaceBinImage = toBin(laplaceImage, ImageParams.binThreshold, ImageParams.binMaxValue);
                if (ImageShowStatus.showBin)
                    tabManager.addTabPageWithPicBox("Laplace变换二值图", laplaceBinImage.Bitmap);
                else tabManager.remove("Laplace变换二值图");

                //Image<Gray, byte> sobelImage = sobel(pretreatmentImage, 0, 1, 9);
                //tabManager.addTabPageWithPicBox("sobel变换", sobelImage.Bitmap);
                //Image<Gray, byte> sobelBinImage = toBin(sobelImage, 150, 255);
                //tabManager.addTabPageWithPicBox("sobel变换二值图", sobelBinImage.Bitmap);
                //利用laplace变换后的图像找边缘
                Image<Gray, byte> laplaceBinCannyImage = toBin(toCanny(laplaceImage, ImageParams.cannyThresh, ImageParams.cannyThreshLinking), ImageParams.binThreshold, ImageParams.binMaxValue);
                //tabManager.addTabPageWithPicBox("拉普拉斯边缘二值化图像", laplaceBinCannyImage.Bitmap);
                //VectorOfVectorOfPoint vvp2 = null;
                //Image<Gray, byte> contoursLaplaceImage = findContours(binImage, ref vvp2);



                // Log.debug("霍夫圆检测");
                CircleF[] circles = null;
                //Image<Bgr, byte> circleImage = findCircle(pretreatmentImage, ref circles, 200, 120, 2.0, pretreatmentImage.Width / 8, 0, Math.Min(pretreatmentImage.Width / 2, pretreatmentImage.Height / 2), new FilterImpl());
                //ImageParams.circles = circles;
                //tabManager.addTabPageWithPicBox("圆检测结果", circleImage.Bitmap);
                //Image<Bgr, byte> circleImage2 = findCircle2(binCannyImage, ref circles);
                //tabManager.addTabPageWithPicBox("圆检测结果2", circleImage2.Bitmap);

                //Image<Gray, byte> skelatanizeImage = skelatanize(pretreatmentImage.Bitmap);//.Dilate(2) - skelatanize(pretreatmentImage.Bitmap).Dilate(1);
                //tabManager.addTabPageWithPicBox("细化图像", skelatanizeImage.Bitmap);

                LineSegment2D[] lines = null;
                //Image<Bgr, byte> lineImage = findLine(toBin(cannyImage, 100, 255), ref lines, 1.0, Math.PI / 45.0, 60, 100, 50, new FilterImpl());
                //tabManager.addTabPageWithPicBox("直线测结果", lineImage.Bitmap);


                //Point[] poinsts = new Point[] { new Point(5000, 5000), new Point(0, 100), new Point(0, 150) };
                //Image<Bgr, byte> dataImage = drawData(poinsts,5000,5000);
                //tabManager.addTabPageWithPicBox("数据", dataImage.Bitmap);


                //Image<Bgr, byte> circleImage4 = findCircle3((laplaceImage), vvp2, ref circles);
                //tabManager.addTabPageWithPicBox("随机抽样圆检测Laplace", circleImage4.Bitmap);
                VectorOfVectorOfPoint vvp3 = null;
                Image<Gray, byte> contoursImage3 = findContours(laplaceBinImage, ref vvp3);
                if(ImageShowStatus.showContour)
                    tabManager.addTabPageWithPicBox("Lapace二值化轮廓图", contoursImage3.Bitmap);
                else tabManager.remove("Lapace二值化轮廓图");

                //大于10说明已经确定了圆心和半径，直接显示，不在进行圆检测
                if (ImageParams.circlesWithSomeFrame.Count == 10)
                {
                    Image<Bgr, byte> circleImage3 = grayImage.Copy().Convert<Bgr, byte>();
                    circleImage3.Draw(ImageParams.circles[0], new Bgr(Color.Red));
                    if (ImageShowStatus.showCircle)
                        tabManager.addTabPageWithPicBox("RSCD圆检测", circleImage3.Bitmap);
                    else tabManager.remove("RSCD圆检测");
                }
                else
                {
                    Image<Bgr, byte> circleImage3 = null;
                    if (ImageParams.meterType==ImageParams.MeterType.CIRCLE)
                        circleImage3 = findCircle3(binCannyImage, vvp3, ref circles);
                    if(ImageParams.meterType==ImageParams.MeterType.NOT_CIRCLE)
                        circleImage3 = findCircle4(binCannyImage, vvp3, ref circles);

                    if (ImageShowStatus.showCircle)
                        tabManager.addTabPageWithPicBox("RSCD圆检测", circleImage3.Bitmap);
                    else tabManager.remove("RSCD圆检测");
                    if (ImageParams.circles == null)
                        ImageParams.circles = new CircleF[1];
                    ImageParams.circles[0] = locationClockDial(circles[0]);
                }

                //Image<Bgr, byte> circleImage5 = findCircle4(binCannyImage, vvp3, ref circles);
                //tabManager.addTabPageWithPicBox("圆弧圆检测结果", circleImage5.Bitmap);
                //径向直线检测依赖于圆心，所以必须定位到圆心才进行检测

                if (ImageParams.circles.Length > 0)
                {
                    if (AppConstant.pointerLocationType == AppConstant.PointerLocationType.RGS)
                    {
                        Image<Bgr, byte> lineImage2 = lineByRGS(grayImage, ImageParams.circles[0], ref ImageParams.lines);
                        if (ImageShowStatus.showLine)
                            tabManager.addTabPageWithPicBox("RGS直线检测", lineImage2.Bitmap);
                        else tabManager.remove("RGS直线检测");
                    }
                    if (AppConstant.pointerLocationType == AppConstant.PointerLocationType.CTM)
                    {
                        Image<Bgr, byte> lineImage2 = lineByCTM(grayImage, ImageParams.circles[0], ref ImageParams.lines);
                        if(ImageShowStatus.showLine)
                            tabManager.addTabPageWithPicBox("CTM直线检测", lineImage2.Bitmap);
                        else tabManager.remove("CTM直线检测");
                    }
                    if (AppConstant.pointerLocationType == AppConstant.PointerLocationType.Hough)
                    {
                            Image<Bgr, byte> lineImage2 = lineByHough(grayImage, ref ImageParams.lines);
                        if (ImageShowStatus.showLine)
                            tabManager.addTabPageWithPicBox("Hough直线检测", lineImage2.Bitmap);
                        else tabManager.remove("Hough直线检测");
                    }
                }
                if (lines != null && lines.Length >= 2)
                {
                    //Image<Bgr, byte> compositePointerImage = compositePointer(toGray(lineImage), lines[0], lines[1], ImageParams.circles[0].Center);
                    //tabManager.addTabPageWithPicBox("合成指针", compositePointerImage.Bitmap);
                }


                //刻度线
                VectorOfVectorOfPoint vvp4 = null;
                Image<Bgr, byte> calibrationLineCountours = findScaleContours(toBin(laplaceImage, ImageParams.binThreshold, ImageParams.binMaxValue), ref vvp4, new FilterImpl());
                //TODO 有问题
                if (ImageShowStatus.showScaleContour)
                    tabManager.addTabPageWithPicBox("刻度线轮廓图", calibrationLineCountours.Bitmap);
                else tabManager.remove("刻度线轮廓图");
                // LineSegment2D[] lines2 = null;
                //Image<Bgr, byte> calibrationLine = findLine(toBin(calibrationLineCountours, 100, 255), ref lines2,1,Math.PI/45,20,1,20);
                //tabManager.addTabPageWithPicBox("Hough刻度线拟合", (calibrationLineCountours + image).Bitmap);

                //绘制在一张图上
                Image<Bgr, byte> finaleImage = grayImage.Copy().Convert<Bgr, byte>();
                int w = 2;//线条宽度
                //绘制圆或刻度弧线
                CircleF circle = ImageParams.circles[0];
                if (ImageParams.pointStart.X != 0 && ImageParams.pointStart.Y != 0 && ImageParams.pointEnd.X != 0 && ImageParams.pointEnd.Y != 0)
                {
                    //计算起始点和终止点与圆心与水平方向的角度
                    double ang1 = Math.Atan2(ImageParams.pointStart.Y - circle.Center.Y, ImageParams.pointStart.X - circle.Center.X) * 180 / Math.PI;// Util.AngleOfPoint(circle.Center, ImageParams.pointStart, new PointF(circle.Center.X+100, circle.Center.Y));
                    double ang2 = Math.Atan2(ImageParams.pointEnd.Y - circle.Center.Y, ImageParams.pointEnd.X - circle.Center.X) * 180 / Math.PI;
                    //半径与圆的交点
                    PointF p1 = new PointF((float)(circle.Center.X + circle.Radius * Math.Cos(Math.PI * ang1 / 180)), (float)(circle.Center.Y + circle.Radius * Math.Sin(Math.PI * ang1 / 180)));
                    PointF p2 = new PointF((float)(circle.Center.X + circle.Radius * Math.Cos(Math.PI * ang2 / 180)), (float)(circle.Center.Y + circle.Radius * Math.Sin(Math.PI * ang2 / 180)));
                    finaleImage.Draw(new LineSegment2DF(circle.Center, p1), new Bgr(Color.Red), w);
                    finaleImage.Draw(new LineSegment2DF(circle.Center, p2), new Bgr(Color.Red), w);

                    //Log.error(ang1 + "," + ang2);
                    if(ImageParams.meterType == ImageParams.MeterType.CIRCLE)
                        CvInvoke.Ellipse(finaleImage, Point.Round(circle.Center), new Size((int)circle.Radius, (int)circle.Radius), 0, /*360 + //如果刻度盘夹角小于180度则需加上360*/ang1, 360 + ang2, new MCvScalar(0, 0, 255), w, LineType.FourConnected, 0);
                    if (ImageParams.meterType == ImageParams.MeterType.NOT_CIRCLE)
                        CvInvoke.Ellipse(finaleImage, Point.Round(circle.Center), new Size((int)circle.Radius, (int)circle.Radius), 0, /*360 + //如果刻度盘夹角小于180度则需加上360*/ang1+360, 360 + ang2, new MCvScalar(0, 0, 255), w, LineType.FourConnected, 0);
                    //Log.debug(ang1 + "," + ang2);
                }
                else
                {
                    finaleImage.Draw(circle, new Bgr(Color.Red), w);
                }
                //圆心
                finaleImage.Draw(new CircleF(ImageParams.circles[0].Center, w), new Bgr(Color.Red), 5);
                //指针
                LineSegment2D pointer = ImageParams.lines[0];
                finaleImage.Draw(pointer, new Bgr(Color.Yellow), w);
                tabManager.addTabPageWithPicBox("结果", finaleImage.Bitmap);
                //计算偏移角度
                double angle1 = Util.AngleOfPoint(circle.Center, ImageParams.pointStart, pointer.P2);
                double angle2 = 0;
                //计算刻度角度(刻度角度小于180度时不加180)
                if (ImageParams.meterType == ImageParams.MeterType.CIRCLE)
                    angle2 = 180 + Util.AngleOfPoint(circle.Center, ImageParams.pointStart, ImageParams.pointEnd);
                if (ImageParams.meterType == ImageParams.MeterType.NOT_CIRCLE)
                    angle2 = Util.AngleOfPoint(circle.Center, ImageParams.pointStart, ImageParams.pointEnd);
                //示数
                double data = angle1 * (ImageParams.range / angle2);
                data = Math.Round(data, 2);
                long end = DateTime.Now.Millisecond;

                //Log.info(angle1 + "," + angle2);
                ImageParams.data = data;
                Form1.form.textBox1.Text = data + "";
                //Log.info("读数:" + data);
                //if (ImageParams.range != 0)
                //{
                //    Form1.form.tipLable.ForeColor = Color.Green;
                //    Form1.form.tipLable.Text = "读数完成";
                //}
                //else
                //{
                //    Form1.form.tipLable.ForeColor = Color.Red;
                //    Form1.form.tipLable.Text = "请先确认量程";
                //}
                //Util.delay(AppConstant.delay);


                if (!AppConstant.isVedio || AppConstant.isCancel)
                {
                    break;
                }
                //Log.info("耗时:"+(end-start)+"ms");
            }
        }
       
        /// <summary>
        /// 定位表盘
        /// </summary>
        /// <param name="circle"></param>
        /// <returns></returns>
        private CircleF locationClockDial(CircleF circle)
        {
            if (AppConstant.testClockDial)
            {
                if (ImageParams.circlesWithSomeFrame.Count == 10)
                {
                    PointF[] pfs = new PointF[ImageParams.circlesWithSomeFrame.Count];
                    float sumR = 0;
                    int n = 0;
                    foreach (CircleF c in ImageParams.circlesWithSomeFrame)
                    {
                        pfs[n] = c.Center;
                        sumR += c.Radius;
                    }
                    AppConstant.testClockDial = false;
                    return new CircleF(Util.AvrPoint(pfs), sumR / ImageParams.circlesWithSomeFrame.Count);
                }
                else
                {
                    ImageParams.circlesWithSomeFrame.Add(circle);
                    return circle;
                }
            }
            else
            {

                return circle;
            }
        }
        /// <summary>
        /// 预处理
        /// </summary>
        public Image<Gray, byte> pretreatment(Image<Bgr, byte> image)
        {
            Log.debug("灰度化处理...");
            Image<Gray, byte> gray = image.Convert<Gray, byte>();
            //this.pretreatmentImage = gray;
            Log.debug("去噪和平滑处理. 次数:" + 10);
            for (int i = 1; i <= 10; i++)
            {
                gray = gray.SmoothMedian(3);
                gray = gray.SmoothBilatral(3, 3, 3);
            }
            // this.pretreatmentImage = gray;
            return gray;
        }
        class FilterImpl : Filter
        {
            public VectorOfVectorOfPoint contoursFilter(VectorOfVectorOfPoint contours)
            {
                VectorOfVectorOfPoint con = new VectorOfVectorOfPoint();
                for (int i = 0; i < contours.Size; i++)
                {

                    VectorOfPoint vp = contours[i];
                    //轮廓点个数过滤
                    if (vp.Size < 5)
                        continue;
                    //面积过滤
                    double area = CvInvoke.ContourArea(vp);
                    //if (area <= 20 || area >= 150)
                    if (area <= 20 || area >= 200)
                        continue;
                    //宽高比过滤
                    RotatedRect rec = CvInvoke.FitEllipse(vp);
                    if (rec.Size.Width / rec.Size.Height >= 0.5 || rec.Size.Width / rec.Size.Height <= 0.1)
                        continue;
                    //位置过滤
                    //外接矩形中心
                    if (Math.Abs(Util.Distance(rec.Center, ImageParams.circles[0].Center) - ImageParams.circles[0].Radius) > 80)
                        continue;
                    if (Util.Distance(rec.Center, ImageParams.circles[0].Center) > ImageParams.circles[0].Radius)
                        continue;
                    //方向过滤
                    float k1 = (rec.Center.Y - ImageParams.circles[0].Center.Y) / (rec.Center.X - ImageParams.circles[0].Center.X);
                    //寻找矩形短边上的中点
                    PointF[] pfs = rec.GetVertices();
                    PointF pp = new PointF();
                    if (Util.Distance(pfs[0], pfs[1]) < Util.Distance(pfs[1], pfs[2]))
                    {
                        pp = new PointF((pfs[0].X + pfs[1].X) / 2, (pfs[0].Y + pfs[1].Y) / 2);
                    }
                    else
                    {
                        pp = new PointF((pfs[1].X + pfs[2].X) / 2, (pfs[1].Y + pfs[2].Y) / 2);
                    }

                    //矩形中心和中点求斜率
                    float k2 = (rec.Center.Y - pp.Y) / (rec.Center.X - pp.X);
                    //Log.debug(k1 + "," + k2);

                    //Log.debug(rec.Size.Width + "," + rec.Size.Height);
                    //Log.debug(Math.Abs(k1 - k2) + "");
                    if (Math.Abs(k1 - k2) > 0.5)
                        continue;
                    con.Push(vp);
                }
                return con;
            }

            public CircleF[] filter(CircleF[] circles)
            {
                if (circles.Length > 0)
                {
                    CircleF[] c = new CircleF[1];
                    //for(int i = 0; i < (circles.Length>=3?3:circles.Length); i++)
                    //{
                    //    c[i] = Util.CircleFSort(circles)[i];
                    //}
                    c[0] = circles[0];
                    return c;
                }
                else
                {
                    return new CircleF[0];
                }
            }

            public LineSegment2D[] lineFilter(LineSegment2D[] lines)
            {
                List<LineSegment2D> lineList = new List<LineSegment2D>();
                if (ImageParams.circles.Length <= 0)
                {
                    return lines;
                }
                Point center = Point.Round(ImageParams.circles[0].Center);
                double r = ImageParams.circles[0].Radius;
                //过滤规则(a)
                double maxLength = 2 * r;
                double minLength = 150;
                //过滤规则(b)
                double threshold = 50;
                //过滤规则(c)
                double maxDistance = 15;
                int n = 0;
                foreach (LineSegment2D line in lines)
                {

                    //长度过滤(a)
                    double length = distance(line.P1, line.P2);
                    if (length < minLength || length > maxLength)
                        continue;
                    //位置过滤(b)
                    double d1 = distance(line.P1, center);
                    double d2 = distance(line.P2, center);
                    if (d1 > r + threshold || d2 > r + threshold)
                        continue;
                    //距离过滤(c)
                    double d = distance(line.P1, line.P2, center);
                    if (d > maxDistance)
                        continue;
                    lineList.Add(line);
                }

                return lineList.ToArray();
            }
        }
        public static double distance(Point p1, Point p2)
        {
            double result = 0;
            result = Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
            return result;
        }
        public static double slope(Point p1, Point p2)
        {
            double result = 0;
            result = ((p1.Y - p2.Y) / (p1.X - p2.X));
            return result;
        }

        public static double distance(PointF pt1, PointF pt2, PointF point)
        {
            double dis = 0;
            if (pt1.X == pt2.X)
            {
                dis = Math.Abs(point.X - pt1.X);
                return dis;
            }
            double lineK = (pt2.Y - pt1.Y) / (pt2.X - pt1.X);
            double lineC = (pt2.X * pt1.Y - pt1.X * pt2.Y) / (pt2.X - pt1.X);
            dis = Math.Abs(lineK * point.X - point.Y + lineC) / (Math.Sqrt(lineK * lineK + 1));
            return dis;

        }
        public static double distance(Point pt1, PointF pt2, Point point)
        {
            double dis = 0;
            if (pt1.X == pt2.X)
            {
                dis = Math.Abs(point.X - pt1.X);
                return dis;
            }
            double lineK = (pt2.Y - pt1.Y) / (pt2.X - pt1.X);
            double lineC = (pt2.X * pt1.Y - pt1.X * pt2.Y) / (pt2.X - pt1.X);
            dis = Math.Abs(lineK * point.X - point.Y + lineC) / (Math.Sqrt(lineK * lineK + 1));
            return dis;

        }

    }
}
